/**
 * 处理话术的拖拽排序(复制自话术库组件)
 * 要求：每个 item 元素必须设置 data-group-id 属性
 *
 *
 * [拖拽处理流程]
 *
 * 鼠标按下，开始监听鼠标事件
 * - 此后若没有松开鼠标键且移动了超过一定距离，判定为用户想要进行拖拽
 * - 若移动没有达到指定距离，就松开了鼠标键，判定为用户想要进行点击，则不做任何事
 *
 * 判定为开始拖拽后
 * - 继续监听鼠标移动，做拖拽响应
 * - 监听鼠标松开事件，代表着拖拽结束
 * - 监听 click 事件，触发了拖拽的情况下则不应再触发其他行为（例如点击超链接弹出新页面），因此要屏蔽浏览器默认行为
 *
 * 拖拽结束
 * - 保存拖拽结果
 */
import { useDragActions, type DroppedInfo } from './actions'

export type { DroppedInfo }

export function useDrag(onDropped: (info: DroppedInfo) => void) {
  const { dragStart, dragMove, dragFinish } = useDragActions(onDropped)

  // 判断拖拽是否开始的检测数据
  const init = useRef<number>(0) // 鼠标起始点击下去时的 Y 坐标
  const prev = useRef<number>(0) // 上一次移动时记录的鼠标 Y 坐标
  const moved = useRef<number>(0) // 鼠标从起始开始总共在 Y 方向移动过多长（先向上 15 再向下 8 认定为移动了 23）
  const dragging = useRef<boolean>(false) // 是否开始执行拖拽

  function onMouseDown(e: React.MouseEvent<HTMLDivElement>) {
    // 初始化检测数据
    init.current = e.clientY
    prev.current = e.clientY
    moved.current = 0
    dragging.current = false

    // 监听鼠标移动和松开事件
    window.addEventListener('mousemove', onMouseMove)
    window.addEventListener('mouseup', onMouseUp, { once: true })
    window.addEventListener('mousewheel', onMouseWheel, { passive: false })
  }

  function onMouseMove(e: MouseEvent) {
    // 屏蔽浏览器原生的拖拽行为
    e.preventDefault()

    // 未判定为拖拽开始前，记录鼠标的移动距离
    // 达到指定距离，则开始处理拖拽
    if (!dragging.current) {
      const stepMoved = Math.abs(e.clientY - prev.current)
      prev.current = e.clientY
      moved.current += stepMoved

      if (moved.current > 5) {
        dragging.current = true
        window.addEventListener('click', () => e.preventDefault(), { once: true }) // 判定为拖拽的情况下忽略后续的 click 事件
        dragStart(e)
      }
    } else {
      // 处理拖拽
      dragMove(e)
    }
  }

  function onMouseWheel(e: Event) {
    // 拖拽期间屏蔽滚动事件
    e.preventDefault()
  }

  function onMouseUp() {
    window.removeEventListener('mousemove', onMouseMove)
    window.removeEventListener('mousewheel', onMouseWheel)
    if (dragging.current) void dragFinish()
  }

  return {
    props: { onMouseDown },
  }
}
